热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开辟|黑体字_数据结构之顺序表

篇首语:本文由编程笔记#小编为大家整理,主要介绍了数据结构之顺序表相关的知识,希望对你有一定的参考价值。 目录 顺序表概念顺序表实现静态顺序表的结构定义动态顺序表的结构定义顺序表初始化顺序表打印检查

篇首语:本文由编程笔记#小编为大家整理,主要介绍了数据结构之顺序表相关的知识,希望对你有一定的参考价值。



目录


  • 顺序表概念
  • 顺序表实现
    • 静态顺序表的结构定义
    • 动态顺序表的结构定义
      • 顺序表初始化
      • 顺序表打印
      • 检查空间进行扩容
      • 顺序表尾插
      • 顺序表尾删
      • 顺序表头插
      • 顺序表头删
      • 顺序表查找
      • 在任意位置插入
      • 在任意位置删除
      • 顺序表销毁


  • 头文件
  • 源文件


顺序表概念

线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,这种表示也称作线性表的顺序存储结构或顺序映像。通常,称这种存储结构的线性表为顺序表(Sequential List)。其特点是,逻辑上相邻的数据元素,其物理次序也是相邻的
线性表的顺序存储结构是一种随机存取的存储结构,一般情况下采用数组存储。
解释下黑体字:假设现在已经实现了每个元素类型为int型的顺序表,并且插入了1、2、3、4、5五个元素。

64位平台下一个整型(int)占4个字节,元素1的后面是2,2的后面是3,我们可以清晰的看到元素1的地址后面就是2的地址,2的地址后面就是3的地址,每两个地址之间就相差它们自身占用空间字节数。
就像这样:


顺序表实现

静态顺序表的结构定义

#define N 500
typedef int SQDataType;
typedef struct SeqList

SQDataType a[N];
int size;
SLT;

静态顺序表在实际中是非常不适用的,空间给小了不够用,给多了可能会浪费,所以最好定义成动态的。


动态顺序表的结构定义

typedef int SQDatatype;
typedef struct SeqList

SQDatatype* a; //指向动态开辟数组的指针
int size; //有效数据的个数
int capacity; //容量空间大小
SLT;

typedef的作用是可以为某一类型自定义名称。编译器会将带有typedef关键字的SQDatatype解释成一个类型的标识符,即int型。这样做的好处是当顺序表中的存储元素是char类型时,只需要将int改为char即可,提高了程序的灵活性。
接下来定义一个结构体,结构体当中有三个成员。
第一个结构体成员为一个指向数组的指针,因为顺序表一般情况下采用数组存储。
第二个结构体成员变量size为数组中有效数据的个数。
假设数组动态开辟了4个整型空间,先插入1,2两个元素,此时有效数据2个,size = 2。

再插入3,4两个元素。

这时会发现我们再想插入数据时数组就越界了&#xff0c;空间不够了&#xff0c;所以需要第三个变量capacity&#xff0c;用来表示容量空间的大小。当有效数据个数size <容量capacity时&#xff0c;随便插入数据&#xff0c;因为数组还有空间&#xff1b;当size &#61;&#61; capacity时说明空间满了&#xff0c;再想插入数据需要扩容。


顺序表初始化

void SeqListInit(SLT* psl)

assert(psl); //psl不可能为NULL&#xff0c;对其进行断言
psl->a &#61; NULL;
psl->size &#61; psl->capacity &#61; 0;

将指针a赋值为NULL&#xff0c;有效数据个数size和容量capacity都赋值为0即可。
为什么函数参数是结构体指针呢&#xff1f;
先看一个简单的例子&#xff1a;

void Swap1(int x, int y)

int temp &#61; x;
x &#61; y;
y &#61; temp;

int main(void)

int a &#61; 10; //变量a
int b &#61; 20; //变量b
Swap1(a, b); //Swap()函数将两个数字交换
printf("a &#61; %d, b &#61; %d\\n", a, b);
return 0;


可以看到打印在屏幕上的结果&#xff0c;两个变量的值并没有交换。

调试发现形参x和y确实交换了&#xff0c;但是a和b的值没有变化&#xff0c;为什么呢&#xff1f;

void Swap2(int* x, int* y)

int temp &#61; *x;
*x &#61; *y;
*y &#61; temp;

int main(void)

int a &#61; 10;
int b &#61; 20;
Swap2(&a, &b);
printf("a &#61; %d, b &#61; %d\\n", a, b);
return 0;


这样a和b的值才会交换。
因为形参只是实参的一份临时拷贝&#xff0c;可以比较一下两次变量的地址。
这是Swap1()函数实参和形参的地址&#xff1a;

可以清晰的看到实参a&#xff0c;b与形参x&#xff0c;y使用的不是同一空间。
这是Swap2()函数实参和形参的地址&#xff1a;

发现实参a&#xff0c;b和形参x&#xff0c;y所用的是同一块空间&#xff0c;所以解引用取到x和y的值并且交换&#xff0c;自然也就交换了a和b的值。
上面改变了两个整型的值&#xff0c;需要传地址参数&#xff0c;结构体也一样&#xff0c;想改变结构体中的内容需要传递结构体的地址&#xff0c;用一个结构体指针接收。
断言
ANSI C实现了一个assert宏&#xff0c;当它被执行时&#xff0c;这个宏对表达式参数进行测试。如果它的值为假(零)&#xff0c;它就向标准错误打印一条诊断信息并终止程序。
这条信息的格式是由编译器定义的&#xff0c;它将包含这个表达式和源文件的名字以及断言所在的行号。如果表达式为真(非零)&#xff0c;将不会打印任何东西&#xff0c;程序继续执行。
初始化函数参数为结构体指针&#xff0c;指向结构体地址&#xff0c;这个地址不可能为空&#xff0c;所以可以对函数参数psl进行断言&#xff0c;如果psl为NULL&#xff0c;就会出现下面的结果&#xff1a;


顺序表打印

void SeqListPrint(SLT* psl)

assert(psl);
for (int i &#61; 0; i < psl->size; &#43;&#43;i)

printf("%d ", psl->a[i]);

printf("\\n");

用一个for循环输出数组中每一个有效数据。


检查空间进行扩容

void CheckCapacity(SLT* psl)

assert(psl);
if (psl->size &#61;&#61; psl->capacity)

size_t newCapacity &#61; psl->capacity &#61;&#61; 0 ? 4 : psl->capacity * 2;
SQDatatype* temp &#61; (SQDatatype*)realloc(psl->a, sizeof(SQDatatype) * newCapacity);
if (temp &#61;&#61; NULL) //如果temp为NULL说明开辟空间失败

printf("realloc fail");
exit(-1); //退出程序

psl->a &#61; temp;
psl->capacity &#61; newCapacity;


有效数据个数size和容量capacity相等需要扩容&#xff0c;如果容量capacity等于0&#xff0c;为其开辟4个整型空间&#xff0c;否则容量变为原来的二倍。
将新空间赋值给a&#xff0c;新容量赋值给capacity。


顺序表尾插

void SeqListPushBack(SLT* psl, SQDatatype x)

assert(psl);
CheckCapacity(psl); //检查是否需要扩容
psl->a[psl->size] &#61; x;
psl->size&#43;&#43;; //有效数据个数加一

要在顺序表尾部进行插入操作&#xff0c;插入元素下标的值和顺序表有效数据个数的值是相等的。

此时有效数据size为2&#xff0c;在2后面尾插个3&#xff0c;那么3的下标就是2&#xff0c;就是size的值。所以psl->a[psl->size] &#61; x&#xff0c;插入完成后有效数据个数变为3个&#xff0c;让size的值加1。


顺序表尾删

void SeqListPopBack(SLT* psl)

assert(psl);
assert(psl->size > 0);
psl->size--;

顺序表有元素的时候才可以进行删除操作&#xff0c;所以对顺序表的有效数据个数进行断言。如果顺序表中有元素&#xff0c;让有效数据个数减一即可。


顺序表头插

void SeqListPushFront(SLT* psl, SQDatatype x)

assert(psl);
CheckCapacity(psl); //检查是否需要扩容
for (int i &#61; psl->size; i > 0; --i)

psl->a[i] &#61; psl->a[i - 1];

psl->a[0] &#61; x; //将元素插入数组第一个位置
psl->size&#43;&#43;; //有效数据个数加一

假设顺序表有1、2、3、4四个元素&#xff0c;此时头插一个x的值为6。

此时size &#61;&#61; capacity&#xff0c;需要扩容。
扩容后&#xff1a;

将数组中的每个元素从最后一个开始赋值个后面一个。
即a[4] &#61; a[3]&#xff0c;a[3] &#61; a[2]











……


直至跳出循环。
循环结束后&#xff1a;

将数组下标为0的位置赋值为6&#xff0c;此时size &#61; 5&#xff0c;capacity &#61; 8。


顺序表头删

void SeqListPopFront(SLT* psl)

assert(psl); //psl不可能为NULL&#xff0c;对其进行断言
assert(psl->size > 0); //存在有效数据才进行删除
for (int i &#61; 0; i < psl->size - 1; &#43;&#43;i)

psl->a[i] &#61; psl->a[i &#43; 1];

psl->size--; //有效数据个数减一

从数组下标为1的元素开始&#xff0c;每个元素都赋值给前一个元素位置&#xff0c;让有效数据个数减一。


顺序表查找

查找顺序表中是否存在某一元素&#xff0c;若存在返回该元素下标&#xff0c;否则返回-1。

int SeqListFind(SLT* psl, SQDatatype x)

assert(psl); //psl不可能为NULL&#xff0c;对其进行断言
for (int i &#61; 0; i < psl->size; &#43;&#43;i)

if (x &#61;&#61; psl->a[i])
return i;

return -1;


在任意位置插入

void SeqListInsert(SLT* psl, size_t pos, SQDatatype x)

assert(psl);
assert(pos >&#61; 0 && pos <&#61; psl->size); //对插入位置pos取值进行断言
CheckCapacity(psl);
for (int i &#61; psl->size; i > pos; --i)

psl->a[i] &#61; psl->a[i - 1];

psl->a[pos] &#61; x; //将x赋值给下标为pos位置
psl->size&#43;&#43;; //有效数据个数加一

假设要在下标为pos的位置插入一个元素&#xff0c;pos的值一定在0和有效数据个数之间&#xff0c;即pos的取值范围[0&#xff0c;size]&#xff0c;pos值为0即头插&#xff0c;pos值为size即尾插。
将pos及其后面的元素向后挪动一位&#xff0c;将要插入的数据x赋值给下标为pos的位置。最后让有效数据的个数加一。


在任意位置删除

void SeqListErase(SLT* psl, size_t pos)

assert(psl); //对psl进行断言
assert(pos >&#61; 0 && pos < psl->size); //对pos取值进行断言
for (int i &#61; pos; i < psl->size - 1; &#43;&#43;i)

psl->a[i] &#61; psl->a[i &#43; 1];

psl->size--; //有效数据个数减一

想在任意位置删除时&#xff0c;pos的取值在[0&#xff0c;size)之间&#xff0c;因为数组最后一个元素的下标为size-1&#xff0c;假如删除size位置&#xff0c;数组越界了&#xff0c;所以这里是开区间。
让pos后面的位置都向前挪动一位&#xff0c;最后让有效数据的个数减一。


顺序表销毁

void SeqListDestory(SLT* psl)

assert(psl);
if (psl->a)

free(psl->a);
psl->a &#61; NULL;

psl->size &#61; psl->capacity &#61; 0;

将数组指针置空&#xff0c;并且将有效数据个数size和容量capacity也置空。


头文件

#pragma once
#include
#include
#include
typedef int SQDatatype;
typedef struct SeqList

SQDatatype* a;
int size; //有效数据的个数
int capacity; //容量
SLT;
//初始化
void SeqListInit(SLT* psl);
//销毁
void SeqListDestory(SLT* psl);
//打印
void SeqListPrint(SLT* psl);
//扩容
void CheckCapacity(SLT* psl);
//尾插
void SeqListPushBack(SLT* psl, SQDatatype x);
//尾删
void SeqListPopBack(SLT* psl);
//头插
void SeqListPushFront(SLT* psl, SQDatatype x);
//头删
void SeqListPopFront(SLT* psl);
//查找
int SeqListFind(SLT* psl, SQDatatype x);
//任意位置插入
void SeqListInsert(SLT* psl, size_t pos, SQDatatype x);
//任意位置删除
void SeqListErase(SLT* psl, size_t pos);

源文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
void SeqListInit(SLT* psl) //初始化

assert(psl);
psl->a &#61; NULL;
psl->size &#61; psl->capacity &#61; 0;

void SeqListDestory(SLT* psl) //销毁

assert(psl);
if (psl->a)

free(psl->a);
psl->a &#61; NULL;

psl->size &#61; psl->capacity &#61; 0;

void SeqListPrint(SLT* psl) //打印

assert(psl);
for (int i &#61; 0; i < psl->size; &#43;&#43;i)

printf("%d ", psl->a[i]);

printf("\\n");

void CheckCapacity(SLT* psl) //检查空间进行扩容

assert(psl);
if (psl->size &#61;&#61; psl->capacity)

size_t newCapacity &#61; psl->capacity &#61;&#61; 0 ? 4 : psl->capacity * 2;
SQDatatype* temp &#61; (SQDatatype*)realloc(psl->a, sizeof(SQDatatype) * newCapacity);
if (temp &#61;&#61; NULL)

printf("realloc fail");
exit(-1);

psl->a &#61; temp;
psl->capacity &#61; newCapacity;


void SeqListPushBack(SLT* psl, SQDatatype x) //尾插

assert(psl);
CheckCapacity(psl);
psl->a[psl->size] &#61; x;
psl->size&#43;&#43;;

void SeqListPopBack(SLT* psl) //尾删

assert(psl);
assert(psl->size > 0);
psl->size--;

void SeqListPushFront(SLT* psl, SQDatatype x) //头插

assert(psl);
CheckCapacity(psl);
for (int i &#61; psl->size; i > 0; --i)

psl->a[i] &#61; psl->a[i - 1];

psl->a[0] &#61; x;
psl->size&#43;&#43;;

void SeqListPopFront(SLT* psl) //头删

assert(psl);
assert(psl->size > 0);
for (int i &#61; 0; i < psl->size - 1; &#43;&#43;i)

psl->a[i] &#61; psl->a[i &#43; 1];

psl->size--;

int SeqListFind(SLT* psl, SQDatatype x) //查找

assert(psl);
for (int i &#61; 0; i < psl->size; &#43;&#43;i)

if (x &#61;&#61; psl->a[i])
return i;

return -1;

void SeqListInsert(SLT* psl, size_t pos, SQDatatype x) //pos位置插入

assert(psl);
assert(pos >&#61; 0 && pos <&#61; psl->size);
CheckCapacity(psl);
for (int i &#61; psl->size; i > pos; --i)

psl->a[i] &#61; psl->a[i - 1];

psl->a[pos] &#61; x;
psl->size&#43;&#43;;

void SeqListErase(SLT* psl, size_t pos) //pos位置删除

assert(psl);
assert(pos >&#61; 0 && pos < psl->size);
for (int i &#61; pos; i < psl->size - 1; &#43;&#43;i)

psl->a[i] &#61; psl->a[i &#43; 1];

psl->size--;


推荐阅读
  • CentOS7源码编译安装MySQL5.6
    2019独角兽企业重金招聘Python工程师标准一、先在cmake官网下个最新的cmake源码包cmake官网:https:www.cmake.org如此时最新 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 本文详细介绍了如何通过多种编程语言(如PHP、JSP)实现网站与MySQL数据库的连接,包括创建数据库、表的基本操作,以及数据的读取和写入方法。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 在金融和会计领域,准确无误地填写票据和结算凭证至关重要。这些文件不仅是支付结算和现金收付的重要依据,还直接关系到交易的安全性和准确性。本文介绍了一种使用C语言实现小写金额转换为大写金额的方法,确保数据的标准化和规范化。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文介绍如何通过创建替代插入触发器,使对视图的插入操作能够正确更新相关的基本表。涉及的表包括:飞机(Aircraft)、员工(Employee)和认证(Certification)。 ... [详细]
  • 本实验主要探讨了二叉排序树(BST)的基本操作,包括创建、查找和删除节点。通过具体实例和代码实现,详细介绍了如何使用递归和非递归方法进行关键字查找,并展示了删除特定节点后的树结构变化。 ... [详细]
  • 本文详细介绍了C语言中链表的两种动态创建方法——头插法和尾插法,包括具体的实现代码和运行示例。通过这些内容,读者可以更好地理解和掌握链表的基本操作。 ... [详细]
  • 本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ... [详细]
  • 根据最新发布的《互联网人才趋势报告》,尽管大量IT从业者已转向Python开发,但随着人工智能和大数据领域的迅猛发展,仍存在巨大的人才缺口。本文将详细介绍如何使用Python编写一个简单的爬虫程序,并提供完整的代码示例。 ... [详细]
  • 本文详细介绍了 MySQL 中 LAST_INSERT_ID() 函数的使用方法及其工作原理,包括如何获取最后一个插入记录的自增 ID、多行插入时的行为以及在不同客户端环境下的表现。 ... [详细]
author-avatar
北辰孤星123
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有